using System;
using System.Collections.Generic;
using UnityEngine.Assertions;

namespace IQIGame.Onigao.Framework
{
    public class ProcedureFSM : Singleton<ProcedureFSM>
    {
        private Dictionary<Type, IProcedure> m_TypeProcedureMap = new Dictionary<Type, IProcedure>();
        private IProcedure m_Current { get; set; }
        public IProcedure current => this.m_Current;

        private ProcedureFSM()
        {

        }

        public void ActivateUpdate()
        {
            FrameworkEntry.MonoDriver.updateEvent += OnUpdate;
        }

        public void AddState<T>() where T : IProcedure, new()
        {
            T procedure = new T();
            procedure.OnInit(this);

            m_TypeProcedureMap[typeof(T)] = procedure;

            if (m_Current == null)
            {
                m_Current = procedure;
            }
        }

        public T GetState<T>() where T : IProcedure
        {
            if (m_TypeProcedureMap.TryGetValue(typeof(T), out var procedure))
            {
                return (T)procedure;
            }
            return default(T);
        }

        private void OnUpdate(float deltaTime, float unscaledDeltaTime)
        {
            m_Current?.OnUpdate(deltaTime, unscaledDeltaTime);
        }

        public void ChangeState<T>() where T : IProcedure
        {
            try
            {
                IProcedure procedure = null;
                m_TypeProcedureMap.TryGetValue(typeof(T), out procedure);
                ChangeState(procedure);
            }
            catch (Exception e)
            {
                UnityEngine.Debug.LogError(e);
            }
        }

        private void ChangeState(IProcedure procedure)
        {
            Assert.IsNotNull(procedure, "Error occured at change procedure. procedure is not found.");
            m_Current?.OnLeave();
            m_Current = procedure;
            m_Current.OnEnter();
        }

        public void Dispose()
        {
            FrameworkEntry.MonoDriver.updateEvent -= OnUpdate;
            foreach (var procedure in m_TypeProcedureMap)
            {
                procedure.Value.OnDestroy();
            }
            m_TypeProcedureMap.Clear();
        }
    }
}
